#pragma once
#include "../utils/geom2d.hpp"
#include "../rescue/graph.hpp"
#include "../graphics/image.hpp"
#include <string>

using namespace geom2d;

class Map {
private:
	//Angles are the angles in radians of each direction.
	static double Angles[4];

public:

	static const int Ndirs = sizeof(Angles)/sizeof(Angles[0]);

	// Rev returns the reverse of a direction.
	static int rev(int d) {
		return (d + Ndirs/2) % Ndirs;
	}

	// Map creates a new map with a single node at 0,0.
	Map();

	Map(const Map&);

	bool operator==(const Map&) const;
	bool operator !=(const Map&) const;

	class Node;
	class Edge;

	// Nnodes returns the number of non-dummy
	// nodes in the map.
	unsigned int nnodes() const;

	// Addnode adds a node with the given ID
	// to the map, returning a reference to it.
	// The reference is invalidated by any
	// subsequent method call on the map.
	Node &addnode(int);

	// Hasnode returns true if the node has
	// been added to the map.
	bool hasnode(int) const;

	// Addedge adds an edge to the map
	// with the given ID, connecting the two
	// given nodes.  The nodes must be initialized
	// before adding an edge between them.
	void addedge(int id, int n, int m);

	// Hasedge returns true if the edge
	// has been added to the map.
	bool hasedge(int) const;

	// Grow grows the map by extending a given
	// number of random nodes with growable edges.
	// The return value is the number of new nodes.
	// If the return value is less than the parameter then
	// the map has been fully populated.
	unsigned int grow(unsigned int);

	// Grownode grows a single node, returning the
	// newly added node ID or -1 if the attempt to
	// grow failed.
	int grownode(int);

	// Draw draws the map to the given file.
	void draw(std::string) const;

	// Rescuegraph returns a Graph object for the
	// rescue domain from this map.
	Graph rescuegraph() const;

	// Neighbors returns a list of all neighbor
	// node IDs.
	std::vector<int> neighbors(int) const;

	// Edge returns a pointer to the edge between
	// n  and m if one exists, otherwise NULL.
	// The pointer is only valid until the next method
	// is called on the map.
	const Edge *edge(int n, int m) const;

	class Node {
	public:
		enum {
			// MayEdge means that there is currently
			// no edge, however, one may be added.
			MayEdge = -1,

			// NoEdge means that there is no edge,
			// and one may not be added.
			NoEdge = -2,
		};

		// Node constructs an empty node with
		// an id of -1, and MayEdge for all edges.
		Node() : id(-1), entrydir(-1), pt(0, 0), c(Image::black), r(2) {
			for (unsigned int i = 0; i < sizeof(e)/sizeof(e[0]); i++)
				e[i] = MayEdge;
		}

		bool operator==(const Node&) const;
		bool operator!=(const Node&) const;

		// Growable returns true if this node may have
		// edges added to it.
		bool growable() const;

		// Id is the index of this node in the nodes vector.
		// It is -1 for uninitialized nodes.
		int id;

		// E are the Edge IDs in the different directions
		// going out from this node, or the constants
		// NoEdge and MayEdge.
		int e[Ndirs];

		// Entrydir is the direction this node was entered
		// upon creation.  It is more likely to be extended
		// in this direction, giving the effect of hallways.
		int entrydir;

		// Pt is the location of this node in 2-space.
		Pt pt;

		// C is the color for drawing this node.
		Color c;

		// R is the point radius for drawing this node.
		double r;
	};

	// Nodes are all nodes in the map.
	std::vector<Node> nodes;

	class Edge {
	public:
		Edge() : id(-1), c(Image::black), w(0.5) { }

		bool operator==(const Edge&) const;
		bool operator!=(const Edge&) const;

		// Id is the index of this edge in the edges
		// vector.  It is -1 for uninitialized edges.
		int id;

		// N is the pair of nodes that are connected
		// by this edge.
		int n[2];

		// Line is the line defining this edge in 2-space.
		LineSg line;

		// C is the color for drawing this edge.
		Color c;

		// W is the line width of the edge;
		double w;
	};

	// Ldges are the line segments defining each edge
	// in the map.
	std::vector<Edge> edges;
};